package a1_p04_jw_kw;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import javax.swing.JApplet;

import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultWeightedEdge;

import a1_p04_jw_kw.graph.GraphCreater;
import a1_p04_jw_kw.graph.GraphParser;
import a1_p04_jw_kw.graph.GraphPersistencer;
import a1_p04_jw_kw.jgraph.JGraphAdapter;
import a1_p04_jw_kw.searchalgo.AltBreadthFirstSearch;
import a1_p04_jw_kw.searchalgo.BreadthRecursiveSearch;
import a1_p04_jw_kw.searchalgo.FullDepthFirstSearch;
import a1_p04_jw_kw.struct.Node;
import a2_p04_jw_kw.graph.GraphNCreater;
import a2_p04_jw_kw.searchalgo.AStar;
import a2_p04_jw_kw.searchalgo.Dijkstra;
import a3_p04_jw_kw.graph.GraphBIGCreater;
import a3_p04_jw_kw.spanningtree.Kruska;
import a3_p04_jw_kw.spanningtree.Prim;
import a3_p04_jw_kw.spanningtree.PrimFibonacciJGraphT;
import a4_p04_jw_kw.euler.Fleury;
import a4_p04_jw_kw.euler.Hierholzer;
import a4_p04_jw_kw.graph.GraphEUBIGCreater;

public class GraphApplet extends JApplet {

	/**
	 *
	 */
	String folder = "../";
	String graphname = "gka4.gka";
	private static final long serialVersionUID = 1L;

	HashMap<String, ArrayList<Node>> bestWayMap = new HashMap<String, ArrayList<Node>>();

	Graph<Node,DefaultWeightedEdge> span;

	GraphNCreater creater;
	GraphBIGCreater gbigc;
	GraphEUBIGCreater gEUbigc;

	Graph<Node, DefaultWeightedEdge> g;
	JGraphAdapter jga;

	public void init() {

		// Algorithmen Erstellung
		// AltBreadthFirstSearch abfs = new AltBreadthFirstSearch(g);
		// BreadthRecursiveSearch bfs = new BreadthRecursiveSearch(g);
		// FullDepthFirstSearch fdsfs = new FullDepthFirstSearch(g);

		// Praktikum 1
		// printAltBreadthFirstSearch(abfs);
		// printBreadthRecursiveSearch(bfs);
		// printFullDepthFirstSearch(fdsfs);

		// Praktikum 2
		//useCaseDiijkstraAStar();
		//useCaseG20(creater, 20, 1, 15);

		// Praktikum 3
//		this.gbigc = new GraphBIGCreater();

		// Praktikum 4
//		this.gEUbigc = new GraphEUBIGCreater(); // nur zum testen der Kantenanzahl erstellt

//		usecaseBIGPrimFile("gka4.gka", "a");
//		usecaseBIGPrimFibonacciFile("gka4.gka", "a");

//		usecaseBIGPrimFile("gka5.gka", "b");
//		usecaseBIGPrimFibonacciFile("gka5.gka", "b");
//		usecaseKruskaFile("gka5.gka", "b");

//		usecaseBIGPrim200();
//		usecaseBIGPrimFibonacci200();
//		usecaseBIGKruska200();

//		usecaseKarsten200(); // nur zum testen der Kantenanzahl erstellt

	    usecaseFileFleury("test.gka");
//	    usecaseFileHierholzer("hierholzer.gka");

//	    usecaseBIGFleury();
//	    usecaseBIGHierholzer();

	}

    private void usecaseFileFleury(String file) {
        g = createGraphByName(folder, file);
        //this.jga = new JGraphAdapter(g, "gka4.gka");
        // draw the graph
        //jga.initPaint(null, "", "");

        Fleury fleury = new Fleury(g);
        fleury.createEulerTour();
        System.out.println("============= Eulertour " + file + " Fleury ==============");
        fleury.getStatistic().printStats();
        for (DefaultWeightedEdge edge : fleury.getEulerTour()) {
            System.out.println(edge);

        }
        System.out.println("size: " + fleury.getEulerTour().size());

        //JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
        //jga2.initPaint(null, "", "");

    }

    private void usecaseBIGHierholzer() {
        gEUbigc = new GraphEUBIGCreater();
        g = gEUbigc.getGraph();
        //this.jga = new JGraphAdapter(g, "gka4.gka");
        // draw the graph
        //jga.initPaint(null, "", "");

        Hierholzer hierholzer = new Hierholzer();
        hierholzer.createEulerTour("1", g);
        System.out.println("============= Eulertour BIG Hierholzer ==============");
        hierholzer.getStatistic().printStats();
//        for (DefaultWeightedEdge edge : hierholzer.getEulerTour()) {
//            System.out.println(edge);
//
//        }
        System.out.println("size: " + hierholzer.getEulerTour().size());

        //JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
        //jga2.initPaint(null, "", "");

    }

    private void usecaseFileHierholzer(String file) {
        g = createGraphByName(folder, file);
        //this.jga = new JGraphAdapter(g, "gka4.gka");
        // draw the graph
        //jga.initPaint(null, "", "");

        Hierholzer hierholzer = new Hierholzer();
        hierholzer.createEulerTour("1", g);
        System.out.println("============= Eulertour " + file + " Hierholzer ==============");
        hierholzer.getStatistic().printStats();
//        for (DefaultWeightedEdge edge : hierholzer.getEulerTour()) {
//            System.out.println(edge);
//
//        }

        System.out.println("size: " + hierholzer.getEulerTour().size());

        //JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
        //jga2.initPaint(null, "", "");

    }

    private void usecaseBIGFleury() {
        gEUbigc = new GraphEUBIGCreater();
        g = gEUbigc.getGraph();
        //this.jga = new JGraphAdapter(g, "gka4.gka");
        // draw the graph
        //jga.initPaint(null, "", "");

        Fleury fleury = new Fleury(g);
        fleury.createEulerTour();
        System.out.println("============= Eulertour BIG Fleury ==============");
        fleury.getStatistic().printStats();
//        for (DefaultWeightedEdge edge : fleury.getEulerTour()) {
//            System.out.println(edge);
//
//        }
        System.out.println("size: " + fleury.getEulerTour().size());

        //JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
        //jga2.initPaint(null, "", "");

    }

	private void usecaseKarsten200() {
		g = this.gEUbigc.getGraph();
		//this.jga = new JGraphAdapter(g, "gka4.gka");
		// draw the graph
		//jga.initPaint(null, "", "");

		Kruska kruska = new Kruska(g);
		kruska.createSpanningTree();
		this.span = kruska.getSpanningTree();
		System.out.println("============= Spanning Tree 200 Kruska ==============");
		System.out.println("Kantengewichtung: " + kruska.getEdgeWeightSum());
		kruska.getStatistic().printStats();
		hasOneOddNode(g);

		//JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
		//jga2.initPaint(null, "", "");

	}

	private void usecaseBIGPrimFile(String file, String start) {
		g = createGraphByName(folder, file);
		//this.jga = new JGraphAdapter(g, "gka4.gka");
		// draw the graph
		//jga.initPaint(null, "", "");

		Prim prim = new Prim(g, start);
		prim.createSpanningTree();
		this.span = prim.getSpanningTree();
		System.out.println("============= Spanning Tree " + file + " Prim PriorityQueue ==============");
		System.out.println("Kantengewichtung: " + prim.getEdgeWeightSum());
		prim.getStatistic().printStats();

		//JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
		//jga2.initPaint(null, "", "");

	}

	private void usecaseBIGPrim200() {
		g = this.gbigc.getGraph();
		//this.jga = new JGraphAdapter(g, "gka4.gka");
		// draw the graph
		//jga.initPaint(null, "", "");

		Prim prim = new Prim(g,"1");
		prim.createSpanningTree();
		this.span = prim.getSpanningTree();
		System.out.println("============= Spanning Tree 200 Prim PriorityQueue ==============");
		System.out.println("Kantengewichtung: " + prim.getEdgeWeightSum());
		prim.getStatistic().printStats();

		//JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
		//jga2.initPaint(null, "", "");

	}

	private void usecaseBIGPrimFibonacciFile(String file, String start) {
		g = createGraphByName(folder, file);
		//this.jga = new JGraphAdapter(g, file);
		// draw the graph
		//jga.initPaint(null, "", "");

		PrimFibonacciJGraphT prim = new PrimFibonacciJGraphT(g,start);
		prim.createSpanningTree();
		this.span = prim.getSpanningTree();
		System.out.println("============= Spanning Tree " + file + " Prim FibonacciHeap ==============");
		System.out.println("Kantengewichtung: " + prim.getEdgeWeightSum());
		prim.getStatistic().printStats();

		//JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
		//jga2.initPaint(null, "", "");

	}

	private void usecaseBIGPrimFibonacci200() {
		g = this.gbigc.getGraph();
		//this.jga = new JGraphAdapter(g, "gka4.gka");
		// draw the graph
		//jga.initPaint(null, "", "");

		PrimFibonacciJGraphT prim = new PrimFibonacciJGraphT(g,"1");
		prim.createSpanningTree();
		this.span = prim.getSpanningTree();
		System.out.println("============= Spanning Tree 200 Prim FibonacciHeap ==============");
		System.out.println("Kantengewichtung: " + prim.getEdgeWeightSum());
		prim.getStatistic().printStats();

		//JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
		//jga2.initPaint(null, "", "");

	}

	private void usecaseBIGKruska200() {
		g = this.gbigc.getGraph();
		//this.jga = new JGraphAdapter(g, "gka4.gka");
		// draw the graph
		//jga.initPaint(null, "", "");

		Kruska kruska = new Kruska(g);
		kruska.createSpanningTree();
		this.span = kruska.getSpanningTree();
		System.out.println("============= Spanning Tree 200 Kruska ==============");
		System.out.println("Kantengewichtung: " + kruska.getEdgeWeightSum());
		kruska.getStatistic().printStats();

		//JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
		//jga2.initPaint(null, "", "");

	}

	private void usecaseKruskaFile(String file, String start) {
		g = createGraphByName(folder, file);
		//this.jga = new JGraphAdapter(g, "gka4.gka");
		// draw the graph
		//jga.initPaint(null, "", "");

		Kruska kruska = new Kruska(g);
		kruska.createSpanningTree();
		this.span = kruska.getSpanningTree();
		System.out.println("============= Spanning Tree " + file + " Kruska ==============");
		System.out.println("Kantengewichtung: " + kruska.getEdgeWeightSum());
		kruska.getStatistic().printStats();

		//JGraphAdapter jga2 = new JGraphAdapter(this.span, "gka4.gka");
		//jga2.initPaint(null, "", "");

	}

	private void useCaseDiijkstraAStar() {
		g = createGraphByName(folder, graphname);
		Dijkstra dijkstraHusum = new Dijkstra(g, "Husum");
		Dijkstra dijkstraMinden = new Dijkstra(g, "Minden");
		Dijkstra dijkstraMuenster = new Dijkstra(g, "M�nster");

		AStar astarHusum = new AStar(g, "Husum", "Hamburg");
		AStar astarMinden = new AStar(g, "Minden", "Hamburg");
		AStar astarMuenster = new AStar(g, "M�nster", "Hamburg");
		this.jga = new JGraphAdapter(g, graphname);

		// Beste Wege f�r Algorithmen
		bestWayMap.put("diHusumBestWay",
				dijkstraHusum.getBestWayToTarget("Hamburg"));
		bestWayMap.put("diMindenBestWay",
				dijkstraMinden.getBestWayToTarget("Hamburg"));
		bestWayMap.put("diMuensterBestWay",
				dijkstraMuenster.getBestWayToTarget("Hamburg"));

		bestWayMap.put("aStarHusumBestWay", astarHusum.getBestWayToTarget());
		bestWayMap.put("aStarMindenBestWay", astarMinden.getBestWayToTarget());
		bestWayMap.put("aStarMuensterBestWay",
				astarMuenster.getBestWayToTarget());

		// draw the graph
		jga.initPaint(bestWayMap, "Dijkstra", "Husum");

		// Praktikum 2
		printDijkstra(dijkstraHusum, dijkstraMinden, dijkstraMuenster);
		printAStar(astarHusum, astarMinden, astarMuenster);
	}

	private void useCaseG20(GraphNCreater creater, int nodes, int start, int target) {
		g = createNGraph(nodes, start, target);
		Dijkstra g20 = new Dijkstra(g);
		AStar g20astar = new AStar(g, "1", "15");
		this.jga = new JGraphAdapter(g, graphname);
		bestWayMap.put("diG20", g20.getBestWayToTarget(Integer.toString(target)));
		bestWayMap.put("aStarG20", g20astar.getBestWayToTarget());
		jga.initPaint(bestWayMap, "Dijkstra", "G20");
		// //save the graph !only for demonstration!
		printG20(g20, g20astar);
		saveG20();
	}

	public void printDijkstra(Dijkstra husum, Dijkstra minden, Dijkstra muenster) {
		System.out.println("============= Dijkstra Algorithmus ==============");
		System.out.println("Husum -> Hamburg");
		System.out.println("\tLaenge bis zum Ziel: "
				+ husum.getLenghtToTarget("Hamburg"));
		System.out.println("\tWeg: " + bestWayMap.get("diHusumBestWay"));
		System.out.println("\tUntersuchte Knoten: " + husum.getCount());
		//
		System.out.println("Minden -> Hamburg");
		System.out.println("\tLaenge bis zum Ziel: "
				+ minden.getLenghtToTarget("Hamburg"));
		System.out.println("\tWeg: " + bestWayMap.get("diMindenBestWay"));
		System.out.println("\tUntersuchte knoten: " + minden.getCount());

		System.out.println("Muenster -> Hamburg");
		System.out.println("\tLaenge bis zum Ziel: "
				+ muenster.getLenghtToTarget("Hamburg"));
		System.out.println("\tWeg: " + bestWayMap.get("diMuensterBestWay"));
		System.out.println("\tUntersuchte knoten: " + muenster.getCount());
	}

	public void printAStar(AStar husum, AStar minden, AStar muenster) {
		System.out.println("============= AStar Algorithmus ================");
		System.out.println("Husum -> Hamburg");
		System.out.println("\tUntersuchte Knoten: " + husum.getCount());
		System.out.println("\tL�nge zum Ziel: " + husum.getLenghtToTarget());
		System.out.println("\tBester Weg Husum -> Hamburg:"
				+ bestWayMap.get("aStarHusumBestWay"));

		System.out.println("Minden -> Hamburg");
		System.out.println("\tUntersuchte Knoten: " + minden.getCount());
		System.out.println("\tL�nge zum Ziel: " + minden.getLenghtToTarget());
		System.out.println("\tBester Weg Minden -> Hamburg:"
				+ bestWayMap.get("aStarMindenBestWay"));
		// System.out.println("L�ngen: " + astarMinden.getPathlength());

		System.out.println("M�nster -> Hamburg");
		System.out.println("\tUntersuchte Knoten: " + muenster.getCount());
		System.out.println("\tL�nge zum Ziel: " + muenster.getLenghtToTarget());
		System.out.println("\tBester Weg M�nster -> Hamburg:"
				+ bestWayMap.get("aStarMuensterBestWay"));
	}

	public void printFullDepthFirstSearch(FullDepthFirstSearch fdsfs) {
		// use the FullDepthFirstSearch !only for demonstration!
		System.out.println("\n Vollst�ndige Tiefensuche: \n");

		fdsfs.startSearch("a", "f");
		System.out.println("--- Vollst. Tiefensuche neu Ende ---\n");
	}

	public void printBreadthRecursiveSearch(BreadthRecursiveSearch bfs) {
		System.out.println("\nrekursive Breitensuche\n");
		bfs.startSearch("a", "f");
		System.out.println("--- Breitensuche neu Ende ---\n");
	}

	public void printAltBreadthFirstSearch(AltBreadthFirstSearch abfs) {
		System.out.println("\nBreitensuche Alternative:\n");
		abfs.startSearch("a", "f");
		System.out.println(abfs.bestWayToString());
		System.out.println("--- Breitensuche Alt Ende ---\n");
	}

	public void printG20(Dijkstra g20, AStar g20astar) {
		// AUFGABE 2 GRAPH MIT 20 KNOTEN
		String t1 = "============= 20er KNOTEN DIJKSTRA";
		System.out.println(t1+fillBarUpTo(t1.length(), 50));

		System.out.println("\tLaenge bis zum Ziel: "
				+ g20.getLenghtToTarget("15"));
		System.out.println("\tWeg: " + g20.getBestWayToTarget("15"));
		System.out.println("\tUntersuchte knoten: " + g20.getCount());

		String t2 = "============= 20er KNOTEN A STAR";
		System.out.println(t2+fillBarUpTo(t2.length(), 50));
		System.out.println("\tLaenge bis zum Ziel: "
				+ g20astar.getLenghtToTarget());
		System.out.println("\tWeg: " + g20astar.getBestWayToTarget());
		System.out.println("\tUntersuchte knoten: " + g20astar.getCount());
	}

	public Graph<Node, DefaultWeightedEdge> createGraphByName(
			String folder, String name) {
		// load some graph
		GraphParser gp = new GraphParser(folder + name);

		// create the graph as JGraphT graph
		GraphCreater gc = new GraphCreater(gp.getGraphSetup());
		gc.createGraph();
		return gc.getGraph();
	}

	public Graph<Node, DefaultWeightedEdge> createNGraph(
			int vertices, int start, int target) {
		// creates a graph with 20 vertices
		this.creater = new GraphNCreater(vertices, start, target);
		return creater.getGraph();
	}

	private void saveG20() {
		GraphPersistencer per = new GraphPersistencer(creater.getGraphSetup(),
				g);
		per.saveGraph();
	}

	private String fillBarUpTo(int lange, int upTo){
		String s = "";
		for (int i = lange; i < upTo; i++) {
			s+= "=";
		}
		return s;
	}

	protected boolean hasOneOddNode(Graph<Node, DefaultWeightedEdge> g){

		Set<DefaultWeightedEdge> edgeSet = g.edgeSet();
		Set<Node> vertexSet = g.vertexSet();
		HashMap<Node, Integer> countList = new HashMap<Node, Integer>();

		System.out.println("Anzahl Knoten:\t"+vertexSet.size() + "\tAnzahl Kanten:\t"+edgeSet.size());

		// initializiere Z�hlliste
		for (Node node : vertexSet) {
			countList.put(node, 0);
		}

		// iteriere �ber alle Kanten
		// sowohl an Start als auch an Endknoten wird die Kantenzahl 1 hochgez�hlt
		for (DefaultWeightedEdge edge : edgeSet) {
		Node start = g.getEdgeSource(edge);
		Integer sValue = countList.get(start);
		countList.put(start, (sValue +1) );

		Node end = g.getEdgeTarget(edge);
		Integer eValue = countList.get(end);
		countList.put(end, (eValue +1) );
		}

		for (Node node : countList.keySet()) {
			System.out.println("countList entry at " + node.name + " is :" + countList.get(node));
			if((countList.get(node) % 2) != 0){
				return true;
			}
		}

		return false;
	}

}
